import qs from '@/node_modules/qs'


/**
 * 构造树型结构数据
 * @param {*} data 数据源
 * @param {*} id id字段 默认 'id'
 * @param {*} parentId 父节点字段 默认 'parentId'
 * @param {*} children 孩子节点字段 默认 'children'
 */
export function handleTree(data, id, parentId, children) {
	const config = {
		id: id || 'id',
		parentId: parentId || 'parentId',
		childrenList: children || 'children',
	}

	const childrenListMap = {}
	const nodeIds = {}
	const tree = []

	for (const d of data) {
		const parentId = d[config.parentId]
		if (childrenListMap[parentId] == null) {
			childrenListMap[parentId] = []
		}
		nodeIds[d[config.id]] = d
		childrenListMap[parentId].push(d)
	}

	for (const d of data) {
		const parentId = d[config.parentId]
		if (nodeIds[parentId] == null) {
			tree.push(d)
		}
	}

	for (const t of tree) {
		adaptToChildrenList(t)
	}

	function adaptToChildrenList(o) {
		if (childrenListMap[o[config.id]] !== null) {
			o[config.childrenList] = childrenListMap[o[config.id]]
		}
		if (o[config.childrenList]) {
			for (const c of o[config.childrenList]) {
				adaptToChildrenList(c)
			}
		}
	}
	return tree
}

// 树形数据结构扁平化
export function flattenTree(tree, childField = 'child') {
	const result = []

	function flatten(node, childField) {
		node.forEach((item) => {
			let res = JSON.parse(JSON.stringify(item)) // 先克隆一份数据作为第一层级的填充
			delete res[childField]
			result.push(res)
			if (item[childField] && item[childField].length > 0) {
				flatten(item[childField], childField)
			}
		})
	}

	flatten(tree, childField)
	return result
}

/**
 * 在树形结构中查找包含指定值的节点及其所有父级节点 (兼容小程序)
 * @param {Array|Object} tree - 树形数据 (数组或单个根节点对象)
 * @param {*} targetValue - 要查找的目标值
 * @param {string} [valueKey='value'] - 节点中存储值的属性名 (默认 'value')
 * @param {string} [childrenKey='children'] - 节点中存储子节点的属性名 (默认 'children')
 * @returns {Array} 包含目标节点及其所有父级节点的数组 (从根到目标节点的路径)，如果未找到则返回空数组
 * 
 * @example
 * const tree = [
 *   { id: 1, value: 'A', children: [
 *     { id: 2, value: 'B', children: [
 *       { id: 3, value: 'C' }
 *     ]},
 *     { id: 4, value: 'D' }
 *   ]},
 *   { id: 5, value: 'E' }
 * ];
 * 
 * const path = findNodeAndParents(tree, 'C');
 * // path = [ {id:1, value:'A', ...}, {id:2, value:'B', ...}, {id:3, value:'C'} ]
 */
export function findNodeAndParents(tree, targetValue, valueKey = 'value', childrenKey = 'children') {
	// 存储从根节点到目标节点的路径
	const path = [];

	/**
	 * 深度优先搜索 (DFS) 递归函数
	 * @param {Array|Object} currentNode - 当前遍历的节点或节点数组
	 * @returns {boolean} 是否在当前分支找到了目标节点
	 */
	function dfs(currentNode) {
		// 处理节点是数组的情况
		if (Array.isArray(currentNode)) {
			for (let i = 0; i < currentNode.length; i++) {
				const node = currentNode[i];
				// 将当前节点加入路径
				path.push(node);
				// 在当前节点及其子树中搜索
				if (dfs(node)) {
					return true; // 找到了，停止搜索
				}
				// 没找到，回溯：移除当前节点
				path.pop();
			}
			return false; // 整个数组都没找到
		}

		// 处理节点是对象的情况
		if (currentNode && typeof currentNode === 'object') {
			// 检查当前节点的值是否匹配
			if (currentNode[valueKey] === targetValue) {
				return true; // 值匹配，找到目标节点！
			}

			// 如果当前节点有子节点，递归搜索子节点
			const children = currentNode[childrenKey];
			if (children && Array.isArray(children) && children.length > 0) {
				for (let i = 0; i < children.length; i++) {
					const child = children[i];
					// 将子节点加入路径
					path.push(child);
					// 在子节点中搜索
					if (dfs(child)) {
						return true; // 在子树中找到了
					}
					// 没找到，回溯：移除子节点
					path.pop();
				}
			}
		}

		// 当前节点不匹配，且其子树中也没找到
		return false;
	}

	// 启动搜索
	dfs(tree);

	// 如果找到了，path 数组就包含了从根到目标节点的完整路径
	// 如果没找到，path 会是空数组
	return path;
}


/**
 * 将数字四舍五入到指定小数位，并移除结果末尾的零。
 * 如果结果以小数点结尾，也会移除该小数点。
 *
 * @param number 需要进行四舍五入的数字。
 * @param digits 指定保留的小数位数。
 * @return 返回四舍五入后的字符串，移除了末尾的零和可能的小数点。
 */
export function myToFixed(number, digits) {
	let value = number
	// 检查是否为字符串
	if (typeof value === 'string') {
		// 尝试将字符串转换为数字
		const number = Number(value)

		// 检查转换结果是否为有效数字
		if (!isNaN(number)) {
			return number
		} else {
			console.warn('无法将提供的字符串转换为数字: ', value)
			return value // 或者可以选择返回 null, false 等表示转换失败
		}
	} else {
		// 如果不是字符串，直接返回原值
		return value
	}
	let result = value.toFixed(digits)
	while (result.endsWith('0')) {
		result = result.slice(0, -1)
	}
	if (result.endsWith('.')) {
		result = result.slice(0, -1)
	}
	return result
}

// 回显数据字典
export function selectDictLabel(datas, value, fieldNames) {
	fieldNames = {
		value: 'value',
		label: 'desc',
		...fieldNames
	}
	if (value === undefined) {
		return ''
	}
	const actions = []
	Object.keys(datas).some((key) => {
		if (datas[key][fieldNames.value] == '' + value) {
			actions.push(datas[key][fieldNames.label])
			return true
		}
	})
	if (actions.length === 0) {
		actions.push(value)
	}
	return actions.join('')
}

export function roundAndPad(number, decimalPlaces) {
	// 首先，使用 toFixed 方法确保小数点后的位数，并四舍五入
	let roundedNumber = Number(number).toFixed(decimalPlaces)

	// 如果需要返回数值而非字符串，可以将其转换回数值
	// 注意：这里我们直接返回字符串形式，因为数值类型会自动去除尾部的零
	return roundedNumber
}
// 小驼峰转下划线函数
export const camelToSnake = (obj) => {
	if (obj === null || typeof obj !== 'object') return obj

	if (Array.isArray(obj)) {
		return obj.map((item) => camelToSnake(item))
	}

	return Object.keys(obj).reduce((acc, key) => {
		const newKey = key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
		const value = obj[key]

		acc[newKey] = typeof value === 'object' ? camelToSnake(value) : value
		return acc
	}, {})
}

// 下划线转小驼峰函数
export const snakeToCamel = (obj) => {
	if (obj === null || typeof obj !== 'object') return obj

	if (Array.isArray(obj)) {
		return obj.map((item) => snakeToCamel(item))
	}

	return Object.keys(obj).reduce((acc, key) => {
		const newKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
		const value = obj[key]

		acc[newKey] = typeof value === 'object' ? snakeToCamel(value) : value
		return acc
	}, {})
}

// 秒数转换
export function formatTime(seconds) {
	if (typeof seconds !== 'number' || seconds < 0) {
		throw new Error('请输入有效的非负秒数')
	}

	const mins = Math.floor(seconds / 60) // 计算分钟数
	const secs = seconds % 60 // 计算剩余的秒数

	// 使用 padStart 确保分钟和秒数都是两位数
	return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
}

// 对象转url需要的查询字符串格式
export function objToParams(data) {
	if (Object.keys(data).length === 0) return data
	let queryString = qs.stringify(data);
	return queryString
}

// 添加一个辅助函数用于反序列化深度对象
export const parseQueryParams = (queryParams) => {
	const result = {};

	for (const key in queryParams) {
		if (queryParams.hasOwnProperty(key)) {
			let value = queryParams[key];

			// 尝试解析 JSON 字符串
			try {
				value = JSON.parse(decodeURIComponent(value));
			} catch (e) {
				// 如果解析失败，使用原始值
				value = decodeURIComponent(value);
			}

			result[key] = value;
		}
	}

	return result;
}

// 获取url参数
export function getUrlQuery(key) {
	const urlParams = new URLSearchParams(window.location.search);
	const urlVal = urlParams.get(key);
	if (urlVal) {
		return urlVal;
	}
}

/**
 * string转array
 */
export function strToArr(val, rule = ',') {
	// 处理 null 或 undefined
	if (val === null || val === undefined) {
		return []
	}

	// 如果已经是数组，直接返回
	if (Array.isArray(val)) {
		return val
	}

	// 判断是否为基本类型
	const type = typeof val

	if (type === 'number') {
		if (isNaN(val)) {
			return [] // 或者 throw new TypeError('NaN is not allowed');
		}
		val = val.toString()
	} else if (type === 'boolean') {
		val = val.toString()
	} else if (type !== 'string') {
		// 非字符串/数字/布尔值等非基础类型，抛出错误或返回空
		return [] // 或者 throw new TypeError(`Unsupported type: ${type}`);
	}

	// 确保 rule 是字符串或正则表达式
	if (typeof rule !== 'string' && !(rule instanceof RegExp)) {
		rule = ','
	}

	return val.split(rule)
}

// 获取单位
export function extractUnit(value) {
	// 使用正则表达式匹配数字后的单位部分
	const match = value.toString().trim().match(/(\d+\.?\d*)([a-z%]*)$/)

	if (match && match[2]) {
		// 如果有单位，返回单位
		return match[2]
	} else {
		// 如果没有明确的单位，默认假设为像素(px)
		return 'px'
	}
}

// 小程序获取环境信息 开发、体验、正式
export function getMpEnvSync() {
	// #ifdef MP-ALIPAY
	try {
		const res = my.getAccountInfoSync();
		const envVersion = res.miniProgram.envVersion || 'unknown';
		return envVersion
	} catch (error) {
		return 'unknown';
	}
	// #endif
	// #ifdef MP-WEIXIN
	const env = typeof __wxConfig !== 'undefined' ? __wxConfig.envVersion : 'unknown';
	return env
	// #endif
}

/**
 * 将数字格式化为金额字符串
 *
 * @param {number|string} num - 要格式化的数字
 * @param {boolean} [showDecimal=true] - 是否显示小数部分，默认显示
 * @returns {string} 格式化后的金额字符串
 */
export function formatToMoney(num, showDecimal = true) {
	// 确保输入为数字或可转换为数字的字符串
	if (typeof num === 'string') {
		num = parseFloat(num)
		if (isNaN(num)) return '0' + (showDecimal ? '.00' : '')
	}

	if (typeof num !== 'number' || isNaN(num)) return '0' + (showDecimal ? '.00' : '')

	// 分离整数和小数部分
	const [integerPart, decimalPart] = num.toFixed(showDecimal ? 2 : 0).split('.')

	// 使用正则表达式添加千分位逗号
	const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',')

	// 拼接结果
	return showDecimal ? `${formattedInteger}.${decimalPart}` : formattedInteger
}

/**
 * 递归修改对象/数组中指定字段的值
 * @param {Object|Array} data - 要处理的数据（对象或数组）
 * @param {Array<string>} fields - 要修改的字段名数组
 * @param {Function} modifyFn - 修改函数，接收原值并返回新值
 */
export function modifyFields(data, fields, modifyFn) {
	if (Array.isArray(data)) {
		data.forEach((item) => modifyFields(item, fields, modifyFn))
	} else if (typeof data === 'object' && data !== null) {
		for (let key in data) {
			if (Object.prototype.hasOwnProperty.call(data, key)) {
				if (fields.includes(key)) {
					data[key] = modifyFn(data[key], key)
				} else {
					modifyFields(data[key], fields, modifyFn) // 递归处理子对象
				}
			}
		}
	}
	return data
}

// 通过url获取路径的参数
export const urlToObj = function(url) {
	let obj = {}
	let str = url.slice(url.indexOf('?') + 1)
	let arr = str.split('&')
	for (let j = arr.length, i = 0; i < j; i++) {
		let arr_temp = arr[i].split('=')
		obj[arr_temp[0]] = arr_temp[1]
	}
	return obj
}

/**
 * 防抖原理：一定时间内，只有最后一次操作，再过wait毫秒后才执行函数
 * 
 * @param {Function} func 要执行的回调函数 
 * @param {Number} wait 延时的时间
 * @param {Boolean} immediate 是否立即执行 
 * @return null
 */
export function debounce(func, wait = 500, immediate = false) {
	let timeout; // 将timeout变量移到函数内部，确保每个实例独立

	return function(...args) { // 返回一个新函数
		const context = this;

		// 清除定时器
		if (timeout !== null) clearTimeout(timeout);

		// 立即执行模式
		if (immediate) {
			var callNow = !timeout;
			timeout = setTimeout(function() {
				timeout = null;
			}, wait);
			if (callNow) typeof func === 'function' && func.apply(context, args);
		} else {
			// 设置定时器
			timeout = setTimeout(function() {
				typeof func === 'function' && func.apply(context, args);
			}, wait);
		}
	}
}

/**
 * 深度克隆一个值，支持对象、数组、日期、正则、Map、Set（可选）等
 * @param {*} value
 * @param {WeakMap} [visited] 用于处理循环引用
 * @returns {*}
 */
export const cloneDeep = (value, visited = new WeakMap()) => {
	// 基础类型直接返回
	if (typeof value !== 'object' || value === null) {
		return value;
	}

	// 处理特殊对象类型
	if (value instanceof Date) {
		return new Date(value);
	}

	if (value instanceof RegExp) {
		return new RegExp(value);
	}

	// 处理 Map（可选）
	if (value instanceof Map) {
		const mapCopy = new Map();
		for (const [key, val] of value.entries()) {
			mapCopy.set(cloneDeep(key, visited), cloneDeep(val, visited));
		}
		return mapCopy;
	}

	// 处理 Set（可选）
	if (value instanceof Set) {
		const setCopy = new Set();
		for (const item of value) {
			setCopy.add(cloneDeep(item, visited));
		}
		return setCopy;
	}

	// 避免循环引用
	if (visited.has(value)) {
		return visited.get(value);
	}

	// 创建新的对象或数组
	const result = Array.isArray(value) ? [] : {};
	visited.set(value, result);

	// 递归复制属性
	for (const key in value) {
		if (Object.prototype.hasOwnProperty.call(value, key)) {
			result[key] = cloneDeep(value[key], visited);
		}
	}

	return result;
};

/**
 * 解析时间并格式化为指定格式的字符串
 * @param {Date|number|string} time - 需要解析的时间，可以是Date对象、时间戳或时间字符串
 * @param {string} pattern - 格式化模式，默认为'{y}-{m}-{d} {h}:{i}:{s}'
 * @returns {string|null} 格式化后的时间字符串，如果时间无效则返回null
 */
export function parseTime(time, pattern) {
	if (arguments.length === 0 || !time) {
		return null
	}
	const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
	let date
	if (typeof time === 'object') {
		date = time
	} else {
		if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
			time = parseInt(time, 10)
		} else if (typeof time === 'string') {
			// 修复正则表达式写法以兼容小程序
			time = time.replace(/-/g, '/')
				.replace('T', ' ')
				.replace(/\.[\d]{3}/g, '')
		}
		// 修复数字转换以提高兼容性
		if (typeof time === 'number' && ('' + time).length === 10) {
			time = time * 1000
		}
		date = new Date(time)
	}

	// 增加日期有效性检查，提高小程序兼容性
	if (!date || date.toString() === 'Invalid Date' || isNaN(date.getTime())) {
		return null
	}

	const formatObj = {
		y: date.getFullYear(),
		m: date.getMonth() + 1,
		d: date.getDate(),
		h: date.getHours(),
		i: date.getMinutes(),
		s: date.getSeconds(),
		a: date.getDay()
	}
	const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
		let value = formatObj[key]
		// Note: getDay() returns 0 on Sunday
		if (key === 'a') {
			return ['日', '一', '二', '三', '四', '五', '六'][value]
		}
		if (result.length > 0 && value < 10) {
			value = '0' + value
		}
		return value || 0
	})
	return time_str
}

/**
 * 复制内容到剪贴板
 * @param {string} content - 需要复制的内容
 */
export function copy(content) {
	// 判断是否在小程序环境
	const isMiniProgram = typeof wx !== 'undefined' && wx.showToast

	if (isMiniProgram) {
		// 小程序环境使用小程序API
		wx.setClipboardData({
			data: content,
			success: function() {
				wx.showToast({
					title: '复制成功',
					icon: 'success',
					duration: 2000
				})
			},
			fail: function() {
				wx.showToast({
					title: '复制失败',
					icon: 'none',
					duration: 2000
				})
			}
		})
	} else {
		// Web环境使用原有逻辑
		// 创建元素用于复制
		const aux = document.createElement('input')
		// 设置元素内容
		aux.setAttribute('value', content)
		// 将元素插入页面进行调用
		document.body.appendChild(aux)
		// 复制内容
		aux.select()
		// 将内容复制到剪贴板
		document.execCommand('copy')
		// 删除创建元素
		document.body.removeChild(aux)

		message.success('复制成功')
	}
}

/**
 * 将图片（网络/本地/base64）保存到相册
 * @param {string} src - 图片路径、网络地址 或 base64 字符串
 * @returns {Promise}
 */
export const saveImageToAlbum = (src) => {
	return new Promise((resolve, reject) => {
		// 1. 判断是否是 Base64
		if (src.startsWith('data:image')) {
			// 处理 Base64
			handleBase64(src)
				.then(tempFilePath => saveImage(tempFilePath))
				.then(resolve)
				.catch(reject);
		}
		// 2. 判断是否是网络图片
		else if (src.startsWith('http')) {
			// 下载网络图片
			uni.downloadFile({
				url: src,
				success: (res) => {
					if (res.statusCode === 200) {
						saveImage(res.tempFilePath);
					} else {
						uni.showToast({
							title: '下载失败',
							icon: 'none'
						});
						reject(new Error('下载失败'));
					}
				},
				fail: (err) => {
					uni.showToast({
						title: '下载失败',
						icon: 'none'
					});
					reject(err);
				}
			});
		}
		// 3. 本地路径（如本地文件、_doc、_www 等）
		else {
			saveImage(src);
		}

		// 封装统一保存方法
		function saveImage(filePath) {
			uni.saveImageToPhotosAlbum({
				filePath: filePath,
				success: () => {
					uni.showToast({
						title: '已保存到相册',
						icon: 'success'
					});
					resolve(filePath);
				},
				fail: (err) => {
					// 常见错误：用户拒绝、权限未开
					console.error('保存失败:', err);
					let message = '保存失败';
					if (err.errMsg.includes('cancel')) {
						message = '用户取消操作';
					} else if (err.errMsg.includes('auth')) {
						message = '请在设置中开启相册权限';
					}
					uni.showToast({
						title: message,
						icon: 'none',
						duration: 2000
					});
					reject(err);
				}
			});
		}

		// 处理 Base64 转临时路径（仅小程序支持）
		function handleBase64(base64) {
			return new Promise((resolve, reject) => {
				// 使用 uniapp 插件或自定义方法将 base64 转为临时路径
				// 注意：微信小程序需使用 wx.base64ToArrayBuffer
				const arrayBuffer = uni.base64ToArrayBuffer(base64.split(',')[1]);

				const fileName = `${Date.now()}.png`; // 可根据 mime 判断
				const filePath = `${wx.env.USER_DATA_PATH}/${fileName}`; // 小程序临时路径

				const fs = uni.getFileSystemManager();
				fs.writeFile({
					filePath: filePath,
					data: arrayBuffer,
					encoding: 'binary',
					success: () => {
						resolve(filePath);
					},
					fail: (err) => {
						reject(err);
					}
				});
			});
		}
	});
};